package com.mmd.base.aspect;

import com.mmd.base.annotation.Locks;
import com.mmd.core.enums.ResultEnum;
import com.mmd.base.exception.ApplicationException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Aspect
@Component
public class LockAOP {
    @Autowired
    private RedissonClient redissonClient;

    private final Lock lock = new ReentrantLock();

    @Pointcut("@annotation(com.mmd.base.annotation.Locks)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes,"请求异常,请联系管理员!");
        HttpServletRequest request = attributes.getRequest();
        Assert.notNull(request, "获取请求失败,请联系管理员!");
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Locks annotation = method.getAnnotation(Locks.class);
        if(annotation.isLocal()){
            String lockName = "";
            if(StringUtils.isEmpty(annotation.value())){
                if (method.getDeclaringClass().isAnnotationPresent(Locks.class)) {
                    lockName = method.getDeclaringClass().getName();
                }
                if (method.isAnnotationPresent(Locks.class)) {
                    lockName = method.getName();
                }
            } else {
                lockName = annotation.value();
            }
            //加锁 超时时间：10秒，最好设为常量
            RLock lock = redissonClient.getLock(lockName);
            try {
                if (lock.tryLock(annotation.await(),annotation.timeout(), annotation.timeUnit())) {
                    return pjp.proceed();
                }
            }finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            if (lock.tryLock(annotation.await(), annotation.timeUnit())) {
                try {
                    return pjp.proceed();
                }finally {
                    lock.unlock();
                }
            }
        }
        throw new ApplicationException(ResultEnum.FAIL_REQUEST_QUEUE);
    }
}

